home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
aminet
/
mus
/
play
/
sparctracker100.lha
/
SparcTracker
/
str.c
< prev
Wrap
C/C++ Source or Header
|
1992-10-28
|
15KB
|
517 lines
/*****************************************************************************/
/* */
/* str.c - plays sound/noisetracker files on a SparcStation */
/* */
/* Author : Liam Corner - zenith@dcs.warwick.ac.uk */
/* */
/* Usage : str <filename> */
/* [f|z]cat filename | str */
/* */
/* ------------------------------------------------------------------------- */
/* */
/* New Features added by Rochus Wessels - srb103@math.uni-muenster.de */
/* Version : 1.00r - 28 Oct 1992 */
/* */
/* 27 Oct 92: -v Option added */
/* 28 Oct 92: automatically decrunch .Z-Files */
/* additional Options -d,-h */
/* */
/*****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <strings.h>
#include <fcntl.h>
#include "/usr/demo/SOUND/multimedia/libaudio.h"
#include "/usr/demo/SOUND/multimedia/audio_device.h"
/**********************************************************/
/* uS is the number of uSeconds that a byte is played for */
/* Sparc plays at 8000 bytes/sec => 1 byte = 125 uSec */
/* VSYNC is the number of bytes played in 1/50 sec */
/* ie 0.02/(uS * 10**-6) */
/**********************************************************/
#define uS 125
#define VSYNC 160
#define AUDIO "/dev/audio"
#define MIN(A,B) ((A)<(B) ? (A) : (B))
#define MAX(A,B) ((A)>(B) ? (A) : (B))
typedef struct { /***********************************/
char *info; /* Sample */
int length; /* Length of sample */
float volume; /* Fractional volume 0-1 (min-max) */
int rep_start; /* Byte offset of repeat start */
int rep_end; /* Byte offset of repeat end */
} Voice; /***********************************/
typedef struct { /**************************/
char sample [64][4]; /* Sample number */
char effect [64][4]; /* Effect number */
unsigned char params [64][4]; /* Effect parameters */
int period [64][4]; /* Period (pitch) of note */
} Pattern; /**************************/
typedef struct { /***********************************************/
char old; /* Sample number of last note */
char samp; /* Sample number of current note */
int pitch; /* Current channel pitch (index to step_table) */
int slide; /* Step size of pitch slide (if any) */
unsigned int pointer; /* Current sample position */
unsigned int step; /* Sample offset increment (gives pitch) */
float volume; /* Fractional volume of current note */
} Channel; /***********************************************/
/*****************************************************************************/
/* Skips the next 'n' input bytes - because fseek won't work on stdin */
/*****************************************************************************/
void byteskip (fp, bytes)
FILE *fp;
int bytes;
{
int loop;
for (loop=0; loop<bytes; loop++)
fgetc(fp);
}
/************************************************************************/
/* For routine 'cvt' only */
/************************************************************************/
/* Copyright 1989 by Rich Gopstein and Harris Corporation */
/************************************************************************/
unsigned int cvt(ch)
int ch;
{
int mask;
if (ch < 0) {
ch = -ch;
mask = 0x7f;
} else {
mask = 0xff;
}
if (ch < 32) {
ch = 0xF0 | 15 - (ch / 2);
} else if (ch < 96) {
ch = 0xE0 | 15 - (ch - 32) / 4;
} else if (ch < 224) {
ch = 0xD0 | 15 - (ch - 96) / 8;
} else if (ch < 480) {
ch = 0xC0 | 15 - (ch - 224) / 16;
} else if (ch < 992) {
ch = 0xB0 | 15 - (ch - 480) / 32;
} else if (ch < 2016) {
ch = 0xA0 | 15 - (ch - 992) / 64;
} else if (ch < 4064) {
ch = 0x90 | 15 - (ch - 2016) / 128;
} else if (ch < 8160) {
ch = 0x80 | 15 - (ch - 4064) / 256;
} else {
ch = 0x80;
}
return (mask & ch);
}
void printusage(void)
{
fprintf(stderr,"\nUsage: str [<filename>]\n"
"Options:\n"
" -b patnr. : Starts at Pattern <patnr.> (0-63)\n"
" -d : Display Samplenames\n"
" -h : Display this Helppage\n"
" -l : Loop \n"
" -r patnr : Set Restart-Pattern <patnr.> (0-63)\n"
" -s speed : Set initial Speed <speed> (1-15)\n"
" -v vol : Set Volume <vol> (0-100)\n\n");
exit (1);
}
int main (argc, argv)
int argc;
char **argv;
{
FILE *fp, *audio;
int loop;
int notes, note, channel, vsync;
int pat, pat_num;
int byte, bytes;
int step_table [1024];
int speed=6; /* Default speed is 6 */
int end_pattern=0;
char songlength;
char tune [128];
char num_patterns=0;
unsigned char ulaw;
float dummy1,dummy2;
Voice voices [32];
Pattern patterns [64];
Channel ch [4];
int argnum;
int ftype;
#define FTYPE_FILE 0
#define FTYPE_STDIN 1
#define FTYPE_PIPE 2
#define MAX_SLEN 512
char combuf[MAX_SLEN];
int opt_restart=0;
int opt_loop=0;
int opt_begin=0;
int opt_speed=-1;
int opt_display=0;
#if 0
if (argc>2)
{
fprintf(stderr,"Usage: str32 [<filename>]\n");
exit (1);
}
#endif
/***************************************************************************/
/* Creates a table of the byte_step << 16 for a given pitch */
/* The step and pointer are stored << 16 to get accuracy without floats */
/* eg to get double pitch only play every other byte */
/* so step of 0x10000 is normal pitch, 0x8000 is half, 0x20000 is double */
/* Pointer is >> 16 when accessed, so 0x10000 is 1st byte, 0x20000 2nd etc */
/* I have no idea where the other numbers are from, I copied them from */
/* a SoundTracker player for the Acorn Archimedes */
/***************************************************************************/
step_table[0]=0;
for (loop=1; loop<1024; loop++)
{
dummy1=3575872/loop;
dummy2=(dummy1/(1000000/uS))*60000;
step_table[loop]=(int)dummy2;
}
{
double vol=1.0;
int Audio_fd=-1;
int err=0;
int intvol=0;
char option=0;
char *Audio_dev = "/dev/audio";
for (argnum=1;argnum<argc;argnum++)
{
if (argv[argnum][0]!='-')
break;
option=argv[argnum][1];
switch(option)
{
case 'b':
case 'r':
case 's':
case 'v':
if (argnum==argc)
{
option=0;
printusage();
break;
}
argnum++;
sscanf(argv[argnum],"%d",&intvol);
break;
default:;
}
switch(option)
{
case 'b': opt_begin=intvol;
if (opt_begin<0 || opt_begin>63)
printusage();
break;
case 'd': opt_display=1;break;
case 'h': printusage();break;
case 'l': opt_loop=1;break;
case 'r': opt_restart=intvol;opt_loop=2;
if (opt_restart<0 || opt_restart>63)
printusage();
break;
case 's': opt_speed=intvol;
if (opt_speed<1 || opt_speed>15)
printusage();
speed=opt_speed;
break;
case 'v':
if (intvol<0 || intvol>100)
printusage();
Audio_fd = open(Audio_dev, O_WRONLY | O_NDELAY);
if (Audio_fd>=0)
{
vol=(double)intvol/100.0;
err=audio_set_play_gain(Audio_fd,&vol);
close(Audio_fd);
}
break;
default:printusage();
}
}
}
ftype=FTYPE_FILE;
if (argc==argnum)
{
fp=stdin;
ftype=FTYPE_STDIN;
}
else
{
#define MAX_SLEN 512
int slen;
char * s;
char combuf[MAX_SLEN];
s=argv[argnum];
slen=strlen(s);
if (slen>=2 && slen<(MAX_SLEN-16))
{
if (s[slen-2]=='.' && s[slen-1]=='Z')
{
strcpy(combuf,"zcat ");
strcat(combuf,s);
ftype=FTYPE_PIPE;
}
}
if (ftype==FTYPE_PIPE)
fp=popen(combuf,"r");
else
fp=fopen(argv[argnum],"r");
}
if (fp==NULL)
{
fprintf(stderr,"str: unable to open tune file %s\n",argv[1]);
exit (1);
}
if (opt_display)
{
char s[22];
int i;
for (i=0;i<20;i++)
s[i]=fgetc(fp);
s[20]=0;
printf("Module: %s\n",s);
}
else
byteskip(fp,20); /* Skips over filename */
/* Reads in the 31 sample-information tables */
for (loop=1; loop<32; loop++)
{
if (opt_display)
{
char s[24];
int i;
for (i=0;i<22;i++)
s[i]=fgetc(fp);
s[22]=0;
printf(" %2d: %s\n",loop,s);
}
else
byteskip(fp,22); /* Skips over sample name */
voices[loop].length=((fgetc(fp)<<8)|fgetc(fp))*2;
fgetc(fp);
voices[loop].volume=fgetc(fp);
voices[loop].volume=MIN(voices[loop].volume, 64);
voices[loop].volume/=64; /* Volume is a fraction */
voices[loop].rep_start=((fgetc(fp)<<8)|fgetc(fp))*2;
voices[loop].rep_end=((fgetc(fp)<<8)|fgetc(fp))*2;
if (voices[loop].rep_end==2)
{
/* If there is no repeat then start and end */
/* are set to the end of the sample */
voices[loop].rep_end=voices[loop].length;
voices[loop].rep_start=voices[loop].length;
}
else
{
/* If there is a repeat then end=start+length, but must be */
/* less than the sample length. Not sure if this is 100% */
/* correct, but it seems to work OK :-) */
voices[loop].rep_end+=voices[loop].rep_start;
voices[loop].rep_end=MIN(voices[loop].rep_end, voices[loop].length);
}
}
voices[0].length=0;
songlength=fgetc(fp);
byteskip(fp,1);
/* Reads in the tune */
for (loop=0; loop<128; loop++)
{
tune[loop]=fgetc(fp);
if (tune[loop]>num_patterns) num_patterns=tune[loop];
}
num_patterns++;
byteskip(fp,4);
for (pat_num=0; pat_num<num_patterns; pat_num++) /* Reads in the patterns */
{
for (notes=0; notes<64; notes++) /* 64 notes per pattern */
{
for (channel=0; channel<4; channel++) /* 4 channels per note */
{
note=(fgetc(fp)<<24) | (fgetc(fp)<<16) | (fgetc(fp)<<8) | fgetc(fp);
(patterns[pat_num]).effect[notes][channel]=(note & 0xF00) >> 8;
(patterns[pat_num]).params[notes][channel]=note & 0xFF;
(patterns[pat_num]).sample[notes][channel]=((note & 0xF000)>>12) | ((note>>24) & 0x10);
(patterns[pat_num]).period[notes][channel]=MIN((note & 0xFFF0000) >> 16, 1023);
}
}
}
/* Stores the samples voices as an array of char */
for (loop=1; loop<32; loop++)
{
voices[loop].info=malloc(voices[loop].length);
if (voices[loop].info==NULL)
{
fprintf(stderr, "str: unable to allocate memory\n");
exit (1);
}
fread(voices[loop].info, 1, voices[loop].length, fp);
}
if (feof(fp))
{
fprintf(stderr,"str: input file may be corrupt\n");
/* exit (1);
*/ }
switch(ftype)
{
case FTYPE_FILE: fclose(fp); break;
case FTYPE_PIPE: pclose(fp); break;
default:;
}
audio=fopen(AUDIO, "w");
if (audio==NULL)
{
fprintf(stderr,"str: unable to access %s\n",AUDIO);
exit (1);
}
for (loop=0; loop<4; loop++)
{
ch[loop].old=0;
ch[loop].pointer=0;
ch[loop].step=0;
ch[loop].volume=0;
ch[loop].pitch=0;
}
do {
for (pat_num=opt_begin; pat_num<songlength; pat_num++)
{
pat=tune[pat_num];
for (notes=0; notes<64; notes++)
{
for (channel=0; channel<4; channel++)
{
ch[channel].samp=patterns[pat].sample[notes][channel];
if (!ch[channel].samp) /* If sample number=0 */
ch[channel].samp=ch[channel].old; /* continue last note */
else
{
ch[channel].volume=voices[ch[channel].samp].volume;
ch[channel].pointer=0;
ch[channel].step=step_table[patterns[pat].period[notes][channel]];
ch[channel].pitch=patterns[pat].period[notes][channel];
}
ch[channel].old=ch[channel].samp;
ch[channel].slide=0;
switch (patterns[pat].effect[notes][channel]) /* Do effects */
{
case 0xF : speed=patterns[pat].params[notes][channel];
break;
case 0xD : end_pattern=1;
break;
case 0xC : ch[channel].volume=MIN((patterns[pat].params[notes][channel]),64);
ch[channel].volume/=64;
break;
case 2 : ch[channel].slide=patterns[pat].params[notes][channel]-1;
break;
case 1 : ch[channel].slide=-patterns[pat].params[notes][channel]-1;
break;
case 0 : break;
default : ;
}
}
if (end_pattern) /* Skip to end of pattern if necessary */
{
notes=64;
end_pattern=0;
}
else
for (vsync=0; vsync<speed; vsync++) /* 1 vsync = 0.02 sec */
{
for (bytes=0; bytes<VSYNC; bytes++) /* 160*125uSec = 0.02 */
{
byte=0;
for (channel=0; channel<4; channel++)
{
if ((ch[channel].pointer>>16) < voices[ch[channel].samp].rep_end)
{
/* byte = sum of (sample byte * volume) for each */
/* of 4 channels which mixes the sounds */
byte+=(int)((voices[ch[channel].samp].info[ch[channel].pointer>>16])*(ch[channel].volume));
ch[channel].pointer+=ch[channel].step;
}
else
/* If at end of sample jump to rep_start position */
ch[channel].pointer=voices[ch[channel].samp].rep_start<<16;
}
ulaw=(unsigned char) cvt(byte*4); /* Convert byte */
fputc(ulaw,audio); /* and play the note */
}
for (channel=0; channel<4; channel++) /* Do end of vsync */
if (ch[channel].slide) /* effects */
{
ch[channel].pitch+=ch[channel].slide;
ch[channel].pitch=MIN(ch[channel].pitch, 1023);
ch[channel].pitch=MAX(ch[channel].pitch, 0);
ch[channel].step=step_table[ch[channel].pitch];
}
}
}
} /* for */
opt_begin=opt_restart;
} while (opt_loop);
fclose(audio);
return (0);
}